package java8.chapter11.test;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class BestPriceFinder {

	private final List<Shop> shops = Arrays.asList(new Shop("BestPrice"), new Shop("LetsSaveBig"), new Shop("MyFavoriteShop"), new Shop("BuyItAll"), new Shop("ShopEasy"));
	
	private final Executor executor = Executors.newFixedThreadPool(shops.size(), new ThreadFactory() {

		@Override
		public Thread newThread(Runnable r) {
			Thread t = new Thread(r);
			t.setDaemon(true);
			return t;
		}
		
	});
	
	public List<String> findPricesSequential(String product) {
		return shops.stream().map(shop -> shop.getPrice(product)).map(Quote::parse).map(Discount::applyDiscount).collect(Collectors.toList());
	}

	public List<String> findPricesParallel(String product) {
		return shops.parallelStream().map(shop -> shop.getPrice(product)).map(Quote::parse).map(Discount::applyDiscount).collect(Collectors.toList());
	}

	public List<String> findPricesFuture(String product) {
		List<CompletableFuture<String>> priceFutures = findPricesStream(product).collect(Collectors.<CompletableFuture<String>>toList());
		
		return priceFutures.stream().map(CompletableFuture::join).collect(Collectors.toList());
	}

	private Stream<CompletableFuture<String>> findPricesStream(String product) {
		return shops.stream().map(shop -> CompletableFuture.supplyAsync(() -> shop.getPrice(product), executor)).map(future -> future.thenApply(Quote::parse))
				.map(future -> future.thenCompose(quote -> CompletableFuture.supplyAsync(() -> Discount.applyDiscount(quote), executor)));
	}

	public void printPricesStream(String product) {
		long start = System.nanoTime();
		@SuppressWarnings("rawtypes")
		CompletableFuture[] futures = findPricesStream(product).map(f -> f.thenAccept(s -> System.out.println(s + " (done in " + ((System.nanoTime() - start) / 1_000_000) + " msecs)")))
				.toArray(size -> new CompletableFuture[size]);
		CompletableFuture.allOf(futures).join();
		System.out.println("All shops have now responded in " + ((System.nanoTime() - start) / 1_000_000) + " msecs");
	}
	
}